﻿using System;

using System.Collections.Generic;
using System.Text;
using Silvermoon.Windows.Styles;
using Silvermoon.Windows;
using System.Diagnostics;

namespace Silvermoon.Controls
{
    /// <summary>
    /// Controls generation an disposal of virtual item container to visualize data in a <see cref="ListView"/>.
    /// </summary>
    /// <typeparam name="C">Type of <see cref="T:ITemContainer"/> that this controller generates to visualize data.</typeparam>
    public class SimpleListViewController<C> : IDisposable where C : Item
    {
        class StyleItemCache : IDisposable
        {
            private readonly List<C>[] Cache = new List<C>[(int)(ItemPlacement.Single) + 1];

            public void Dispose()
            {
                GC.SuppressFinalize(this);
                foreach (var cache in Cache)
                {
                    if (cache != null)
                    {
                        foreach (C item in cache) item.Dispose();
                        cache.Clear();
                    }
                }
            }

            public C GetItem(ItemPlacement placement)
            {
                int index = (int)placement;
                List<C> cache = GetCache(index);
                int n = cache.Count;
                if (n == 0) return null;
                n--;
                C item = cache[n];
                cache.RemoveAt(n);
                return item;
            }

            private List<C> GetCache(int index)
            {
                List<C> cache = Cache[index];
                if (cache == null)
                {
                    cache = new List<C>();
                    Cache[index] = cache;
                }
                return cache;
            }

            public void AddToCache(C item)
            {
                int index = (int)item.ControllerPlacement;
                List<C> cache = GetCache(index);
                cache.Add(item);
            }

        }

        private ListView listView;
        private ItemControlGeneratorEventArgs<C> itemEventArgs = new ItemControlGeneratorEventArgs<C>();
        private StyleItemCache cache = new StyleItemCache();

        public SimpleListViewController(ListView listView)
            : base()
        {
            if (listView == null) throw new ArgumentNullException("listBox");
            this.listView = listView;

            BindListView(listView);
        }



        public virtual void Dispose()
        {
            OnDispose();
            cache.Dispose();
            UnbindListView();
            DisposeStyles();
        }


        /// <summary>
        /// Gets or sets the style for all items.
        /// </summary>
        public StyleBase ItemStyle { get; set; }

        /// <summary>
        /// Gets or sets the style for the first item in the list.
        /// </summary>
        public StyleBase FirstItemStyle { get; set; }

        /// <summary>
        /// Gets or sets the style for the middle (between first and last) item in the list.
        /// </summary>
        public StyleBase MiddleItemStyle { get; set; }

        /// <summary>
        /// Gets or sets the style for the last item in the list.
        /// </summary>
        public StyleBase LastItemStyle { get; set; }

        /// <summary>
        /// Gets or sets the style for the only item in the list (if the list contains only one value).
        /// </summary>
        public StyleBase SingleItemStyle { get; set; }


        protected bool HasItemStyles
        {
            get { return SingleItemStyle != null || FirstItemStyle != null || MiddleItemStyle != null || LastItemStyle != null || FirstItemStyle != null; }
        }

        /// <summary>
        /// Gets the <see cref="ListView"/> which is controlled.
        /// </summary>
        protected ListView ListView { get { return listView; } }

        private void DisposeStyles()
        {
            DisposeIfNotNull(FirstItemStyle);
            DisposeIfNotNull(MiddleItemStyle);
            DisposeIfNotNull(LastItemStyle);
            DisposeIfNotNull(SingleItemStyle);
        }

        protected void DisposeIfNotNull(object obj)
        {
            if (obj != null)
            {
                IDisposable d = obj as IDisposable;
                if (d != null) d.Dispose();
            }
        }


        protected virtual void BindListView(ListView listView)
        {
            listView.Disposed += new EventHandler(OnListViewDisposed);
            listView.GetVirtualItem += new EventHandler<ItemEventArgs>(OnGetItem);
            listView.ItemAttached += OnItemAttached;
            listView.ReleaseVirtualItem += new EventHandler<ItemEventArgs>(OnReleaseItem);
            listView.CommandRaised += new EventHandler<CommandEventArgs>(OnListViewCommand);
        }

        protected virtual void UnbindListView()
        {
            itemEventArgs.Group = null;
            itemEventArgs.Item = null;

            listView.Disposed -= OnListViewDisposed;
            listView.GetVirtualItem -= OnGetItem;
            listView.ItemAttached -= OnItemAttached;
            listView.ReleaseVirtualItem -= OnReleaseItem;
            listView.CommandRaised -= OnListViewCommand;
        }

        protected virtual void OnListViewCommand(object sender, CommandEventArgs e)
        {
        }

        protected virtual void OnListViewDisposed(object sender, EventArgs e)
        {
            Dispose();
        }


        private void OnReleaseItem(object sender, ItemEventArgs e)
        {
            C item = e.Item as C;
            if (item != null)
            {
                OnReleaseItem(item);
                cache.AddToCache(item);
                e.Item = null;
            }
        }




        private void OnGetItem(object sender, ItemEventArgs e)
        {
            bool hasStyle = HasItemStyles;
            ItemPlacement placement = hasStyle ? Item.GetPlacement(e.Host, e.Index) : ItemPlacement.Unknown;

            C item = cache.GetItem(placement);
            if (item == null)
            {
                item = CreateItemInstance();
                item.ControllerPlacement = placement;
                OnInitializeItem(item, e.Index, item.Host as ListViewGroup);
                //if (ItemStyle != null) ItemStyle.Apply(item);
                if (ItemStyle != null) item.Style = ItemStyle;
                if (hasStyle) item.Style = GetStyleFromPlacement(placement);
            }

            e.Item = item;

            //  item.Attach(e.Index, e.Value, listView);
            //OnUpdateItem(item, e.Index, e.Value, item.Host as ListViewGroup);
        }

        protected virtual void OnItemAttached(object sender, ItemEventArgs e)
        {
            var item = e.Item as C;
            if (item != null)
            {
                OnUpdateItem(item, e.Index, item.DataValue, item.Host as ListViewGroup);
            }
        }

        protected virtual C CreateItemInstance()
        {
            return Activator.CreateInstance<C>();
        }


        public StyleBase GetStyleFromPlacement(ItemPlacement placement)
        {
            switch (placement)
            {
                case ItemPlacement.First: return FirstItemStyle;
                case ItemPlacement.Middle: return MiddleItemStyle;
                case ItemPlacement.Last: return LastItemStyle;

                case ItemPlacement.Single: return SingleItemStyle;
                default: return null;
            }
        }

        protected virtual void OnReleaseItem(C item)
        {
            if (ReleaseItem != null)
            {
                ItemControlGeneratorEventArgs<C> e = itemEventArgs;
                e.Group = item.Host as ListViewGroup;
                e.Item = item;
                ReleaseItem(this, e);
            }
        }

        protected void OnDispose()
        {
            if (Disposing != null) Disposing(this, EventArgs.Empty);
        }

        protected virtual void OnInitializeItem(C item, int index, ListViewGroup group)
        {
            if (InitializeItem != null)
            {
                ItemControlGeneratorEventArgs<C> e = itemEventArgs;
                e.Group = group;
                e.Item = item;
                InitializeItem(this, e);
            }
        }

        protected virtual void OnUpdateItem(C item, int index, object value, ListViewGroup group)
        {
            if (UpdateItem != null)
            {
                ItemControlGeneratorEventArgs<C> e = itemEventArgs;
                e.Group = group;
                e.Value = value;
                e.Item = item;
                UpdateItem(this, e);
            }
        }

        public event EventHandler<ItemControlGeneratorEventArgs<C>> UpdateItem;
        public event EventHandler<ItemControlGeneratorEventArgs<C>> InitializeItem;
        public event EventHandler<ItemControlGeneratorEventArgs<C>> ReleaseItem;
        public event EventHandler Disposing;

    }

    public class ItemControlGeneratorEventArgs<C> : EventArgs where C : Control
    {
        public C Item { get; set; }
        public object Value { get; internal set; }
        public ListViewGroup Group { get; internal set; }
    }
}
